package com.vrp3d.security.filter;

import com.alibaba.fastjson.JSONObject;
import com.vrp3d.common.context.InvokerContext;
import com.vrp3d.common.enums.CommonEnum;
import com.vrp3d.common.enums.UserExceptionEnum;
import com.vrp3d.common.response.RestResponse;
import com.vrp3d.common.utils.*;
import com.vrp3d.domain.model.UserContext;
import com.vrp3d.domain.po.User;
import com.vrp3d.security.token.ValidateToken;
import com.vrp3d.service.UserService;
import io.jsonwebtoken.Claims;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.web.filter.authc.AuthenticatingFilter;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.util.StringUtils;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedReader;
import java.io.IOException;

/**
 * 自定义过滤器_token验证
 *
 * @author vrp3d
 */
@Slf4j
public class CustomAuthenticationFilter extends AuthenticatingFilter {

    @Autowired
    private UserService userService;
/*    @Autowired
    private RedisService redisService;*/

    public static final String USERNAME = "username";
    public static final String PASSWORD = "password";
    private static final String TOKEN = CommonEnum.TOKEN_NAME.getMsg();

    private ValidateToken validateToken;

    public CustomAuthenticationFilter() {
        this.validateToken = new ValidateToken();
    }

    @Override
    protected AuthenticationToken createToken(ServletRequest request, ServletResponse response) throws IOException {
        String username;
        String password;
        if (request.getContentType().equals(MediaType.APPLICATION_JSON_VALUE)) {
            //非表单登录
            BufferedReader reader = request.getReader();
            String body = reader.lines().reduce(String::concat).orElseThrow(IllegalArgumentException::new);
            JSONObject object = JSONObject.parseObject(body);
            username = object.getString(USERNAME);
            password = object.getString(PASSWORD);
            if (StringUtils.hasText(username) && StringUtils.hasText(password)) {
                User user = userService.findByMobile(username);
                if (null != user) {
                    password = PasswordEncoderUtil.passwordHash(password, user.getSalt());
                    return createToken(username, password, request, response);
                }
            } else {
                return createToken(username, password, request, response);
            }
        } else {
            //表单登录
            username = request.getParameter(USERNAME);
            password = request.getParameter(PASSWORD);
            if (StringUtils.hasText(username) && StringUtils.hasText(password)) {
                User user = userService.findByMobile(username);
                if (null != user) {
                    password = PasswordEncoderUtil.passwordHash(password, user.getSalt());
                    return createToken(username, password, request, response);
                } else {
                    return createToken(username, password, request, response);
                }
            }
        }
        throw new IllegalArgumentException("请求Content-type错误");
    }

    /**
     * 是否允许通过
     *
     * @param request
     * @param response
     * @param mappedValue
     * @return
     */
    @Override
    protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
        HttpServletRequest httpServletRequest = (HttpServletRequest) request;
        if (isLoginRequest(request, response)) {
            return false;
        }
        String jwtToken = httpServletRequest.getHeader(TOKEN);
        if (!StringUtils.hasText(jwtToken)) {
            jwtToken = httpServletRequest.getParameter(TOKEN);
            if (!StringUtils.hasText(jwtToken)) {
                return false;
            }
        }
        User userFromMysql;
        try {
            Claims claims = JwtUtil.parseJwt(jwtToken);
            String username = claims.get(JwtUtil.USERNAME).toString();
            userFromMysql = userService.findByMobile(username);
            if (ObjUtil.isEmpty(userFromMysql)) {
                userFromMysql = userService.findByEmail(username);
                if (ObjUtil.isEmpty(userFromMysql)) {
                    log.info("token解析正常,但是用户不存在,username:{},token:{}", username, jwtToken);
                    return false;
                }
            }
            /*// user存在,从redis中验证是否唯一token
            String tokenFromRedis = redisService.query(CommonEnum.USER_TOKEN.getMsg() + userFromMysql.getId());
            if (org.apache.commons.lang3.StringUtils.isNotBlank(tokenFromRedis)) {
                if (!tokenFromRedis.equals(jwtToken)) {
                    return false;
                }
            } else {
                return false;
            }*/
        } catch (Exception e) {
            log.error("解析token异常(大概率是过期),token:{},异常信息:{}", jwtToken, e.getMessage());
            return false;
        }
        boolean validateResult = validateToken.validateToken(jwtToken, request);
        if (validateResult) {
            // 用户上下文中存放用户信息
            UserContext userContext = new UserContext();
            BeanUtils.copyProperties(userFromMysql, userContext);
            InvokerContext.setUserCtx(userContext);

            // 用户信息存入cookie中
            String jsonString = JSONObject.toJSONString(userContext);
            CookieUtil.setCookie((HttpServletRequest) request, (HttpServletResponse) response, CommonEnum.COOKIE_USER_INFO.getMsg(), jsonString, DateUtil.howManySecondsUntilMidnight().intValue(), CommonEnum.CODING_TYPE.getMsg());
            return true;
        } else {
            return false;
        }
    }

    /**
     * 未通过处理
     *
     * @param servletRequest
     * @param servletResponse
     * @return
     * @throws Exception
     */
    @Override
    protected boolean onAccessDenied(ServletRequest servletRequest, ServletResponse servletResponse) throws Exception {
        HttpServletRequest request = (HttpServletRequest) servletRequest;
        HttpServletResponse response = (HttpServletResponse) servletResponse;
        if (isLoginRequest(request, response)) {
            if (!request.getMethod().equalsIgnoreCase(POST_METHOD)) {
                setResponse(new RestResponse(RestResponse.RestResult.METHOD_NOT_ALLOWED), response);
                return false;
            }
            return executeLogin(servletRequest, servletResponse);
        }
        setResponse(new RestResponse(UserExceptionEnum.USER_IS_NOT_LOGGED_IN.getCode(), UserExceptionEnum.USER_IS_NOT_LOGGED_IN.getMsg()), response);
        return false;
    }

    /**
     * 登录成功执行方法
     *
     * @param token
     * @param subject
     * @param request
     * @param response
     * @return
     * @throws Exception
     *//*
    @Override
    protected boolean onLoginSuccess(AuthenticationToken token, Subject subject, ServletRequest request, ServletResponse response) throws Exception {
        String jwtToken = JwtUtil.createJwt((String) token.getPrincipal());
        setResponse(new RestResponse().put(jwtToken), response);
        return false;
    }*/

    /**
     * 登录失败执行方法
     *
     * @param token
     * @param e
     * @param request
     * @param response
     * @return
     */
    /*@Override
    protected boolean onLoginFailure(AuthenticationToken token, AuthenticationException e, ServletRequest request, ServletResponse response) {
        RestResponse restResponse = null;
        if (e instanceof UnknownAccountException) {
            restResponse = new RestResponse(UserExceptionEnum.USER_DOES_NOT_EXIST.getCode(), UserExceptionEnum.USER_DOES_NOT_EXIST.getMsg());
        }
        if (e instanceof IncorrectCredentialsException) {
            restResponse = new RestResponse(UserExceptionEnum.WRONG_PASSWORD.getCode(), UserExceptionEnum.WRONG_PASSWORD.getMsg());
        }
        setResponse(restResponse, response);
        return false;
    }*/
    private void setResponse(RestResponse result, ServletResponse response) {
        // 设置响应码
        HttpServletResponse httpServletResponse = (HttpServletResponse) response;
        httpServletResponse.setStatus(HttpServletResponse.SC_UNAUTHORIZED);

        // 解决中文乱码
        response.setContentType("application/json;charset=UTF-8");
        try {
            response.getWriter().write(JSONObject.toJSONString(result));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

}